
/*
 *  V4L2 video capture example
 *
 *  This program can be used and distributed without restrictions.
 */

#include "videoRecord.h"
#include "cmem.h"

#define UDP_STREAM_VIDEO_PORT	25310
#define UDP_VIDEO_PACKET_LEN 1440

static const unsigned char IframeTxt[] = {'I' , 'F' , 'R' , 'M'};

#define CLEAR(x) memset (&(x), 0, sizeof (x))

static void usage (FILE *fp, int argc, char ** argv)
{
        fprintf (fp,
                 "Usage: %s [options]\n\n"
                 "Options:\n"
                 "-d | --device name   Video device name [/dev/video]\n"
                 "-h | --help          Print this message\n"
                 "-x | --width         Image width\n"
				 "-y | --height        Image height\n"
				 "-o | --width2        Second image width\n"
				 "-p | --height2       Second image height\n"
				 "-f | --fps           Image fps\n"
				 "-s | --fps2          Second Image fps\n"
				 "-b | --bitrate       Encode bitrate\n"
				 "-c | --bitrate2      Second image Encode bitrate\n"
				 "-t | --test          Add test image\n"
				 "-n | --nov4l2        Do not get image from V4L2\n"
				 "-j | --jpgfps        Jpeg Image fps\n"
				 "-q | --jpgq          Jpeg Image Q\n"
                 "",
                 argv[0]);
}

static const char short_options [] = "d:hmrux:y:f:b:tnj:q:";

static const struct option
long_options [] = {
        { "device",     required_argument,      NULL,           'd' },
        { "help",       no_argument,            NULL,           'h' },
        { "mmap",       no_argument,            NULL,           'm' },
        { "read",       no_argument,            NULL,           'r' },
        { "userp",      no_argument,            NULL,           'u' },
		{ "width",      required_argument,            NULL,           'x' },
		{ "height",     required_argument,            NULL,           'y' },
		{ "fps",     	required_argument,            NULL,           'f' },
		{ "bitrate",   	required_argument,            NULL,           'b' },
		{ "width2",      required_argument,            NULL,           'o' },
		{ "height2",     required_argument,            NULL,           'p' },
		{ "fps2",     	required_argument,            NULL,           's' },
		{ "bitrate2",   required_argument,            NULL,           'c' },
		{ "jpgfps",     	required_argument,            NULL,           'j' },
		{ "jpgq ",   required_argument,            NULL,           'q' },
		{ "test",   	no_argument,            NULL,           't' },
		{ "nov4l2",   	no_argument,            NULL,           'n' },
        { 0, 0, 0, 0 }
};



VideoRecord::VideoRecord()
{
	io              = IO_METHOD_MMAP;
	fd              = -1;
	width[0] = 1280 ;
	height[0] = 720 ;
	fps[0] = 30 ;
	bitrate[0] = 4000 * 1000;

#if MAX_VENC_STREAM > 1
	width[1] = 320 ;
	height[1] =  180;
	fps[1] = 30 ;
	bitrate[1] = 500 * 1000;
#endif


#if MAX_VENC_STREAM > 2
	fps[2] = 5;
#endif
	jpg_q = 8;
}

		




VideoRecord::~VideoRecord()
{
	
}


void VideoRecord::errno_exit  (const char *s)
{
        fprintf (stderr, "%s error %d, %s\n",
                 s, errno, strerror (errno));

        exit (EXIT_FAILURE);
}

int VideoRecord::xioctl(int  fd,  int  request, void * arg)
{
        int r;

        do r = ioctl (fd, request, arg);
        while (-1 == r && EINTR == errno);

        return r;
}


static void fillblock(int x , int y , unsigned char * p , int Y , int U , int V , int pitch)
{
	int row , col;
	p += x * 2 + y * pitch;
	for(row = 0 ; row < 16 ; ++row)
	{
		for(col = 0 ; col < 32 ; col += 4)
		{
			p[col] = U;
			p[col + 1] = Y;
			p[col + 2] = V;
			p[col + 3] = Y;
		}
		p += pitch;
	}
}

void VideoRecord::encode_small_image(unsigned int input_addr)
{
	void * ds_input_buf;
	unsigned int ds_input_addr;
	int stream = 1;
	unsigned char * output_buf;
	unsigned int keyframe;
	unsigned int output_addr;
	unsigned int output_len = 0;
	int r;
	ds_input_buf = cxon2_encode_get_input_buffer(venc_handle[stream] , &ds_input_addr);
	pp_cfg.ppInImg.bufferBusAddr = input_addr;
	pp_cfg.ppInImg.bufferCbBusAddr = pp_cfg.ppInImg.bufferCrBusAddr = input_addr;
	pp_cfg.ppInImg.bufferBusAddrBot = pp_cfg.ppInImg.bufferBusAddrChBot = input_addr;
	pp_cfg.ppOutImg.bufferBusAddr = ds_input_addr;
	pp_cfg.ppOutImg.bufferChromaBusAddr = ds_input_addr;
	r = PPSetConfig(pp_inst , &pp_cfg);
	r = PPGetResult(pp_inst);
	

	cxon2_encode_image(venc_handle[stream] , ds_input_addr);


	output_buf = (unsigned char  *)cxon2_encode_get_output_data(venc_handle[stream] , &output_addr , &output_len , &keyframe);
	if (keyframe)
	{
		unsigned head_len = 0;
		unsigned char * head_buf;
		head_buf = (unsigned char *)cxon2_encode_get_stream_head(venc_handle[stream] , &head_len);
		udp_send(stream , head_buf , head_len);
	}
	udp_send(stream , output_buf , output_len);
}

void VideoRecord::encode_jpg_image(unsigned int input_addr)
{
	int stream = 2;
	void * output_buf;
	unsigned int keyframe;
	unsigned int output_addr;
	unsigned int output_len = 0;
	unsigned char * udp_buf;
	unsigned int udp_len;

	cxon2_encode_image(venc_handle[stream] , input_addr);

	output_buf = cxon2_encode_get_output_data(venc_handle[stream] , &output_addr , &output_len , &keyframe);
	udp_buf = (unsigned char * )output_buf;
	udp_len = output_len;
	udp_send(stream , udp_buf , udp_len);
	jpg_count++;
}


void VideoRecord::udp_send(int stream , unsigned char * udp_buf , int udp_len)
{
	int r;
	unsigned char recv_buf[8];
	int packet_len;
	do
	{
		r = udp_video_svr[stream].getPacket(recv_buf , sizeof(recv_buf));
		if (r == 4)
		{
			if (memcmp(IframeTxt , recv_buf , 4) == 0)
			{
				forceIFrame(stream);
			}
		}
	}while (r > 0);

	if(dump_file[stream])
	{
		fwrite(udp_buf , 1 , udp_len , dump_file[stream]);
	}
	for (;udp_len > 0;)
	{
		packet_len = udp_len > UDP_VIDEO_PACKET_LEN ? UDP_VIDEO_PACKET_LEN :  udp_len;
		udp_video_svr[stream].sendPacket(udp_buf , packet_len);
		udp_buf += packet_len;
		udp_len -= packet_len;
	}
}

void VideoRecord::run()
{
	unsigned int img_addr = 0;
	while(1)
	{
		pthread_mutex_lock(&smi_mutex);
        pthread_cond_wait(&smi_cond,&smi_mutex);
		img_addr = smi_img_addr;
		smi_img_addr = 0;
		pthread_mutex_unlock(&smi_mutex);
		if(img_addr)
		{
			encode_small_image(img_addr);
		}
	}
}

void VideoRecord::run_jpg()
{
	unsigned int img_addr = 0;
	while(1)
	{
		pthread_mutex_lock(&jpg_mutex);
        pthread_cond_wait(&jpg_cond,&jpg_mutex);
		img_addr = jpg_img_addr;
		jpg_img_addr = 0;
		pthread_mutex_unlock(&jpg_mutex);
		if(img_addr)
		{
			encode_jpg_image(img_addr);
		}
	}
}

void VideoRecord::process_image(const void * p , unsigned buf_index)
{
	unsigned char * output_buf;
	unsigned char * input_buf = (unsigned char *)p;
	unsigned int input_addr;
	unsigned int output_addr;
	unsigned int output_len;
	unsigned int keyframe;
	unsigned char recv_buf[8];
	int send_packet_count = 0;
	int r;
	int i;
	if(image_count > 4)
	{
		int stream = 0;
		if(enable_test_image)
		{
			if(image_count < 8 || !nov4l2)
			{
				for(i = 0 ; i < 32 ; ++i)
				{
					fillblock(i * 16 , 0 , (unsigned char *)input_buf , ((i >> 4) & 1) * 128 + 64  , 
							  ((i >> 2) & 3) * 64 + 32  , (i & 3) * 64 + 32 , width[stream] * 2);
				}
			}
			int pos = (image_count & 31);
			int cy = ((image_count >> 5) & 1) * 128 + 64;
			int cu = ((image_count >> 6) & 3) * 64 + 32;
			int cv = ((image_count >> 8) & 3) * 64 + 32;
			fillblock(pos * 16 , 16 , (unsigned char *)input_buf ,  cy , cu  , cv , width[stream] * 2); 
		}

		input_addr = cxon2_encode_get_phy_addr(venc_handle[stream] , input_buf);

		cxon2_encode_image(venc_handle[stream] , input_addr);

		output_len = 0;
		output_buf = (unsigned char *)cxon2_encode_get_output_data(venc_handle[stream] , &output_addr , &output_len , &keyframe);
		cxon2_encode_write_stream_to_driver(venc_handle[stream] , output_buf ,output_len ,keyframe);
		if (keyframe)
		{
			unsigned head_len = 0;
			unsigned char * head_buf;
			head_buf = (unsigned char *)cxon2_encode_get_stream_head(venc_handle[stream] , &head_len);
			udp_send(0 , head_buf , head_len);
		}
		udp_send(0 , output_buf , output_len);
		

		image_encode_count++;

		pthread_mutex_lock(&smi_mutex);
		
#if MAX_VENC_STREAM > 1
		if((image_count % smi_fps_div) == 0)
		{
			smi_img_addr = 	input_addr;
		}
#endif
		pthread_cond_signal(&smi_cond);
		pthread_mutex_unlock(&smi_mutex);

		
#if MAX_VENC_STREAM > 2
		if((image_count % jpg_fps_div) == 0)
		{
			pthread_mutex_lock(&jpg_mutex);
			jpg_img_addr = 	input_addr;
			pthread_cond_signal(&jpg_cond);
			pthread_mutex_unlock(&jpg_mutex);
		}
#endif
		
		
	}
	if ((image_count & 0x3f) == 16)
	{
		struct timeval cur_time;
		gettimeofday(&cur_time , NULL);
		if (start_time.tv_sec)
		{
			long dif_s = cur_time.tv_sec - start_time.tv_sec;
			long dif_us = cur_time.tv_usec - start_time.tv_usec;
			dif_us += dif_s * 1000 * 1000;
			printf("Video fps = %f\n" , image_encode_count * 1000.0 * 1000 / dif_us);
			fflush(stdout);
		}
		start_time = cur_time;
		image_encode_count = 0;
	}
	image_count++;
}

int VideoRecord::read_frame ()
{
        struct v4l2_buffer buf , buf2;
        unsigned int i;

        switch (io) {
        case IO_METHOD_READ:
                if (-1 == read (fd, buffers[0].start, buffers[0].length)) {
                        switch (errno) {
                        case EAGAIN:
                                return 0;

                        case EIO:
                                /* Could ignore EIO, see spec. */

                                /* fall through */

                        default:
                                errno_exit ("read");
                        }
                }

                process_image (buffers[0].start , 0);

                break;

        case IO_METHOD_MMAP:
                CLEAR (buf);
				

                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_MMAP;
				buf2 = buf;
                if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
                        switch (errno) {
                        case EAGAIN:
                                return 0;

                        case EIO:
                                /* Could ignore EIO, see spec. */

                                /* fall through */

                        default:
                                errno_exit ("VIDIOC_DQBUF");
                        }
                }

                assert (buf.index < n_buffers);

				if(-1 == xioctl (fd, VIDIOC_DQBUF, &buf2))
				{
					process_image (buffers[buf.index].start , buf.index);
					if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
                        errno_exit ("VIDIOC_QBUF");
				}
				else
				{
					if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
							errno_exit ("VIDIOC_QBUF");
					process_image (buffers[buf2.index].start , buf2.index);
					if (-1 == xioctl (fd, VIDIOC_QBUF, &buf2))
							errno_exit ("VIDIOC_QBUF 2");
				}
                
              
                break;

        case IO_METHOD_USERPTR:
                CLEAR (buf);

                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_USERPTR;

                if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
                        switch (errno) {
                        case EAGAIN:
                                return 0;

                        case EIO:
                                /* Could ignore EIO, see spec. */

                                /* fall through */

                        default:
                                errno_exit ("VIDIOC_DQBUF");
                        }
                }

                for (i = 0; i < n_buffers; ++i)
                        if (buf.m.userptr == (unsigned long) buffers[i].start
                            && buf.length == buffers[i].length)
                                break;

                assert (i < n_buffers);

                process_image ((void *) buf.m.userptr , i);

                if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
                        errno_exit ("VIDIOC_QBUF");

                break;
        }

        return 1;
}

void VideoRecord::procOneFrame()
{
	fd_set fds;
	struct timeval tv;
	int r;
	unsigned addr;
	const void * p;
	if(!nov4l2)
	{
		FD_ZERO (&fds);
		FD_SET (fd, &fds);
	
		/* Timeout. */
		tv.tv_sec = 30;
		tv.tv_usec = 0;
	
		r = select (fd + 1, &fds, NULL, NULL, &tv);
	
		if (r > 0)
		{
			read_frame ();
		}
	}
	else
	{
		p = (const void * )cxon2_encode_get_input_buffer(venc_handle[0] , &addr);
		if(p)
		{
			process_image(p , 0);
			usleep(10000);

		}
	}

		
}

void VideoRecord::stopCapturing()
{
        enum v4l2_buf_type type;

        switch (io) {
        case IO_METHOD_READ:
                /* Nothing to do. */
                break;

        case IO_METHOD_MMAP:
        case IO_METHOD_USERPTR:
                type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

                if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))
                        errno_exit ("VIDIOC_STREAMOFF");

                break;
        }
}

void VideoRecord::startCapturing()
{
        unsigned int i;
        enum v4l2_buf_type type;

        switch (io) {
        case IO_METHOD_READ:
                /* Nothing to do. */
                break;

        case IO_METHOD_MMAP:
                for (i = 0; i < n_buffers; ++i) {
                        struct v4l2_buffer buf;

                        CLEAR (buf);

                        buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                        buf.memory      = V4L2_MEMORY_MMAP;
                        buf.index       = i;
                        if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
                                errno_exit ("VIDIOC_QBUF");
                }

                type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
                        errno_exit ("VIDIOC_STREAMON");

                break;

        case IO_METHOD_USERPTR:
                for (i = 0; i < n_buffers; ++i) {
                        struct v4l2_buffer buf;

                        CLEAR (buf);

                        buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                        buf.memory      = V4L2_MEMORY_USERPTR;
                        buf.index       = i;
                        buf.m.userptr   = (unsigned long) buffers[i].start;
                        buf.length      = buffers[i].length;

                        if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
                                errno_exit ("VIDIOC_QBUF");
                }

                type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

                if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
                        errno_exit ("VIDIOC_STREAMON");

                break;
        }
}

void VideoRecord::uninitDevice()
{
        unsigned int i;

        switch (io) {
        case IO_METHOD_READ:
                free (buffers[0].start);
                break;

        case IO_METHOD_MMAP:
                for (i = 0; i < n_buffers; ++i)
                        if (-1 == munmap (buffers[i].start, buffers[i].length))
                                errno_exit ("munmap");
                break;

        case IO_METHOD_USERPTR:
                for (i = 0; i < n_buffers; ++i)
                        free (buffers[i].start);
                break;
        }

        free (buffers);
}

void VideoRecord::initRead(unsigned int buffer_size)
{
        buffers = (struct buffer * )calloc (1, sizeof (*buffers));

        if (!buffers) {
                fprintf (stderr, "Out of memory\n");
                exit (EXIT_FAILURE);
        }

        buffers[0].length = buffer_size;
        buffers[0].start = malloc (buffer_size);

        if (!buffers[0].start) {
                fprintf (stderr, "Out of memory\n");
                exit (EXIT_FAILURE);
        }
}

void VideoRecord::initMmap()
{
        struct v4l2_requestbuffers req;

        CLEAR (req);

        req.count               = 2;
        req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory              = V4L2_MEMORY_MMAP;
        if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {
                if (EINVAL == errno) {
                        fprintf (stderr, "%s does not support "
                                 "memory mapping\n", dev_name);
                        exit (EXIT_FAILURE);
                } else {
                        errno_exit ("VIDIOC_REQBUFS");
                }
        }

        if (req.count < 2) {
                fprintf (stderr, "Insufficient buffer memory on %s\n",
                         dev_name);
                exit (EXIT_FAILURE);
        }

        buffers = (struct buffer * )calloc (req.count, sizeof (*buffers));

        if (!buffers) {
                fprintf (stderr, "Out of memory\n");
                exit (EXIT_FAILURE);
        }

        for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
                struct v4l2_buffer buf;

                CLEAR (buf);

                buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory      = V4L2_MEMORY_MMAP;
                buf.index       = n_buffers;

                if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))
                        errno_exit ("VIDIOC_QUERYBUF");

                buffers[n_buffers].length = buf.length;
                buffers[n_buffers].start =
                        mmap (NULL /* start anywhere */,
                              buf.length,
                              PROT_READ | PROT_WRITE /* required */,
                              MAP_SHARED /* recommended */,
                              fd, buf.m.offset);

                if (MAP_FAILED == buffers[n_buffers].start)
                        errno_exit ("mmap");
        }
}

void VideoRecord::initUserp(unsigned int  buffer_size)
{
        struct v4l2_requestbuffers req;
        unsigned int page_size;

        page_size = getpagesize ();
        buffer_size = (buffer_size + page_size - 1) & ~(page_size - 1);

        CLEAR (req);

        req.count               = 4;
        req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory              = V4L2_MEMORY_USERPTR;

        if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {
                if (EINVAL == errno) {
                        fprintf (stderr, "%s does not support "
                                 "user pointer i/o\n", dev_name);
                        exit (EXIT_FAILURE);
                } else {
                        errno_exit ("VIDIOC_REQBUFS");
                }
        }

        buffers = (struct buffer * )calloc (4, sizeof (*buffers));

        if (!buffers) {
                fprintf (stderr, "Out of memory\n");
                exit (EXIT_FAILURE);
        }

        for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
                buffers[n_buffers].length = buffer_size;
                buffers[n_buffers].start = memalign (/* boundary */ page_size,
                                                     buffer_size);

                if (!buffers[n_buffers].start) {
                        fprintf (stderr, "Out of memory\n");
                        exit (EXIT_FAILURE);
                }
        }
}

void VideoRecord::initDevice()
{
        struct v4l2_capability cap;
        struct v4l2_cropcap cropcap;
        struct v4l2_crop crop;
        struct v4l2_format fmt;
        unsigned int min;

        if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {
                if (EINVAL == errno) {
                        fprintf (stderr, "%s is no V4L2 device\n",
                                 dev_name);
                        exit (EXIT_FAILURE);
                } else {
                        errno_exit ("VIDIOC_QUERYCAP");
                }
        }

        if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
                fprintf (stderr, "%s is no video capture device\n",
                         dev_name);
                exit (EXIT_FAILURE);
        }

        switch (io) {
        case IO_METHOD_READ:
                if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
                        fprintf (stderr, "%s does not support read i/o\n",
                                 dev_name);
                        exit (EXIT_FAILURE);
                }

                break;

        case IO_METHOD_MMAP:
        case IO_METHOD_USERPTR:
                if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
                        fprintf (stderr, "%s does not support streaming i/o\n",
                                 dev_name);
                        exit (EXIT_FAILURE);
                }

                break;
        }


        /* Select video input, video standard and tune here. */


        CLEAR (cropcap);

        cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) {
                crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                crop.c = cropcap.defrect; /* reset to default */

                if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) {
                        switch (errno) {
                        case EINVAL:
                                /* Cropping not supported. */
                                break;
                        default:
                                /* Errors ignored. */
                                break;
                        }
                }
        } else {
                /* Errors ignored. */
        }


        CLEAR (fmt);


        fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmt.fmt.pix.width       = width[0];
        fmt.fmt.pix.height      = height[0];
        fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
        fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;

        if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))
                ;
		

        /* Note VIDIOC_S_FMT may change width and height. */

        /* Buggy driver paranoia. */
        min = fmt.fmt.pix.width * 2;
        if (fmt.fmt.pix.bytesperline < min)
                fmt.fmt.pix.bytesperline = min;
        min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
        if (fmt.fmt.pix.sizeimage < min)
                fmt.fmt.pix.sizeimage = min;

        switch (io) {
        case IO_METHOD_READ:
                initRead (fmt.fmt.pix.sizeimage);
                break;

        case IO_METHOD_MMAP:
                initMmap ();
                break;

        case IO_METHOD_USERPTR:
                initUserp (fmt.fmt.pix.sizeimage);
                break;
        }
}

void VideoRecord::close_device()
{
        if (-1 == close (fd))
                errno_exit ("close");

        fd = -1;
}

void VideoRecord::open_device()
{
        struct stat st;

        if (-1 == stat (dev_name, &st)) {
                fprintf (stderr, "Cannot identify '%s': %d, %s\n",
                         dev_name, errno, strerror (errno));
                exit (EXIT_FAILURE);
        }

        if (!S_ISCHR (st.st_mode)) {
                fprintf (stderr, "%s is no device\n", dev_name);
                exit (EXIT_FAILURE);
        }

        fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);

        if (-1 == fd) {
                fprintf (stderr, "Cannot open '%s': %d, %s\n",
                         dev_name, errno, strerror (errno));
                exit (EXIT_FAILURE);
        }
}




void VideoRecord::setupOn2(int outfmt)
{
	int i;
	for(i = 0 ; i < MAX_VENC_STREAM ; ++i)
	{
		CxOn2EncodeType encode_type = i < 2 ? CXON2_ENC_TYPE_H264 : CXON2_ENC_TYPE_JPG;
		venc_handle[i] = cxon2_encode_init(encode_type , NULL , NULL , width[i] , height[i] , 
										   width[i] , height[i] , 
										   (CxOn2YuvFmt)outfmt ,
					bitrate[i] , 30 , fps[i] , 1 , jpg_q);
		printf("Init venc %d , width = %d height = %d type = %d handle = %p\n " , i , width[i] , 
			   height[i] , encode_type , venc_handle[i]);
		cxon2_encode_start(venc_handle[i]);
		if(i == 0)
		{
			void * head_buf;
			unsigned head_len = 0;
			head_buf = cxon2_encode_get_stream_head(venc_handle[i] , &head_len);
			cxon2_encode_write_stream_head_to_driver(venc_handle[i] , (unsigned char * )head_buf , head_len);
		}
		printf("Start venc\n");
	}

#if MAX_VENC_STREAM > 1
	printf("Compute small image fps div\n");
	if(fps[1] > 0)
	{
		smi_fps_div = fps[0] / fps[1];
		if(smi_fps_div < 1)
		{
			smi_fps_div = 1;
		}
		else if(smi_fps_div > 30)
		{
			smi_fps_div = 30;
		}
	}
	else
	{
		smi_fps_div = 1;
	}
#endif


#if MAX_VENC_STREAM > 2
    printf("Compute jpg image fps div\n");
	if(fps[2] > 0)
	{
		jpg_fps_div = fps[0] / fps[2];
		if(jpg_fps_div < 1)
		{
			jpg_fps_div = 1;
		}
		else if(jpg_fps_div > 30)
		{
			jpg_fps_div = 30;
		}
	}
	else
	{
		jpg_fps_div = 1;
	}
#endif

#if MAX_VENC_STREAM > 1
	PPResult r;
	r = PPInit(&pp_inst);
	printf("PPInit %d %x\n" , r , pp_inst);
	r = PPGetConfig(pp_inst , &pp_cfg);
	printf("PPGetConfig %d %x\n" , r , pp_cfg.ppInImg.pixFormat);
	pp_cfg.ppInImg.pixFormat = PP_PIX_FMT_CBYCRY_4_2_2_INTERLEAVED;
	pp_cfg.ppInImg.width = width[0];
	pp_cfg.ppInImg.height = height[0];
	pp_cfg.ppOutImg.pixFormat = PP_PIX_FMT_CBYCRY_4_2_2_INTERLEAVED;
	pp_cfg.ppOutImg.width = width[1];
	pp_cfg.ppOutImg.height = height[1];
#endif

}

int VideoRecord::startH264Encode(int argc, char  ** argv)
{

	    int i;
        dev_name = strdup("/dev/video0");

        for (;;) {
                int index;
                int c;

                c = getopt_long (argc, argv,
                                 short_options, long_options,
                                 &index);

                if (-1 == c)
                        break;

                switch (c) {
                case 0: /* getopt_long() flag */
                        break;

                case 'd':
                        dev_name = optarg;
                        break;

                case 'h':
                        usage (stdout, argc, argv);
                        exit (EXIT_SUCCESS);

                case 'm':
                        io = IO_METHOD_MMAP;
                        break;

                case 'r':
                        io = IO_METHOD_READ;
                        break;

                case 'u':
                        io = IO_METHOD_USERPTR;
                        break;
				case 'x':
						width[0] = atoi(optarg);
						break;
				case 'y':
						height[0] = atoi(optarg);
						break;
				case 'f':
						fps[0] = atoi(optarg);
						break;
				case 'b':
						bitrate[0] = atoi(optarg);
						break;
#if MAX_VENC_STREAM > 1
				case 'o':
						width[1] = atoi(optarg);
						break;
				case 'p':
						height[1] = atoi(optarg);
						break;
				case 's':
						fps[1] = atoi(optarg);
						break;
				case 'c':
						bitrate[1] = atoi(optarg);
						break;
#endif

#if MAX_VENC_STREAM > 2
				case 'j':
						fps[2] = atoi(optarg);
						break;
#endif

				case 'q':
						jpg_q = atoi(optarg);
						break;
						
				case 't':
						enable_test_image = 1;
						break;
				case 'n':
						nov4l2 = 1;
						break;
                default:
                        usage (stderr, argc, argv);
                        exit (EXIT_FAILURE);
                }
        }

#if MAX_VENC_STREAM > 2
		width[2] = width[0];
		height[2] = height[0];
#endif

		CMEM_init();
		//dump_file[0] = fopen("/ds_0.264" , "wb");
		//dump_file[1] = fopen("/ds_1.264" , "wb");
		//dump_file[2] = fopen("/ds.jpg" , "wb");
        setupOn2(CXON2_ENC_YUV422_INTERLEAVED_UYVY);

		
		if(!nov4l2)
		{
			printf("Init v4l2 driver\n");
			open_device ();
			initDevice ();
			startCapturing ();
		}

		pthread_mutex_init(&smi_mutex,NULL);
        pthread_cond_init(&smi_cond,NULL);
		pthread_create(&threadID, NULL, exec, this);
		

#if MAX_VENC_STREAM > 2
		pthread_mutex_init(&jpg_mutex,NULL);
		pthread_cond_init(&jpg_cond,NULL);
		//pthread_create(&jpg_threadID, NULL, exec_jpg, this);
#endif
		
		for(i = 0 ; i < MAX_VENC_STREAM ; ++i)
		{
			udp_video_svr[i].initServer(UDP_STREAM_VIDEO_PORT + i);
		}
		
		return 0;
}


void VideoRecord::stopH264Encode()
{
	if(!nov4l2)
	{
		stopCapturing ();
		uninitDevice ();
		close_device ();

	}
}

void * VideoRecord::exec(void *arg)
{
	VideoRecord * p = (VideoRecord *)arg;
	p->run();
	return 0;
}


void * VideoRecord::exec_jpg(void *arg)
{
	VideoRecord * p = (VideoRecord *)arg;
	p->run_jpg();
	return 0;
}


void VideoRecord::setRc(int stream , int bitrate , int gop)
{
	cxon2_encode_set_rc(venc_handle[stream] , bitrate , gop);
}

void VideoRecord::forceIFrame(int stream)
{
	cxon2_encode_force_I_frame(venc_handle[stream]);
}

